import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import {
  register,
  Counter,
  Histogram,
  Summary,
  Gauge,
  collectDefaultMetrics,
  Registry,
  Metric,
} from 'prom-client';
import type {
  MonitoringInterface,
  Timer,
  MonitoringConfig,
} from '../../monitoring/monitoring.interface';

/**
 * Prometheus 服务
 * 基于 NestJS 官方示例实现
 * 参考: https://docs.nestjs.cn/fundamentals/dependency-injection
 * 对应 Java: Prometheus 监控
 */
@Injectable()
export class PrometheusService implements MonitoringInterface, OnModuleInit {
  private readonly logger = new Logger(PrometheusService.name);
  private registry: Registry;
  private metrics = new Map<string, Metric>();
  private timers = new Map<string, number>();

  constructor(
    private readonly config: MonitoringConfig = {
      enabled: false,
      port: 9090,
      path: '/metrics',
      prefix: 'wwjcloud',
      defaultLabels: {},
      collectDefaultMetrics: false,
    },
  ) {
    this.registry = new Registry();
    this.initializeRegistry();
  }

  async onModuleInit() {
    if (this.config.collectDefaultMetrics) {
      collectDefaultMetrics({ register: this.registry });
      this.logger.log('Default metrics collection enabled');
    }
  }

  private initializeRegistry() {
    // 设置默认标签
    if (this.config.defaultLabels) {
      this.registry.setDefaultLabels(this.config.defaultLabels);
    }

    // 设置前缀
    if (this.config.prefix) {
      this.registry.setDefaultLabels({
        // ...this.registry.getDefaultLabels(),
        prefix: this.config.prefix,
      });
    }
  }

  /**
   * 记录计数器指标
   */
  counter(
    name: string,
    value: number = 1,
    labels?: Record<string, string>,
  ): void {
    try {
      const metricName = this.getMetricName(name);
      let counter = this.metrics.get(metricName) as Counter<string>;

      if (!counter) {
        counter = new Counter({
          name: metricName,
          help: `Counter metric for ${name}`,
          labelNames: Object.keys(labels || {}),
          registers: [this.registry],
        });
        this.metrics.set(metricName, counter);
      }

      counter.inc(labels || {}, value);
    } catch (error) {
      this.logger.error(`Failed to record counter: ${name}`, error);
    }
  }

  /**
   * 记录直方图指标
   */
  histogram(
    name: string,
    value: number,
    labels?: Record<string, string>,
  ): void {
    try {
      const metricName = this.getMetricName(name);
      let histogram = this.metrics.get(metricName) as Histogram<string>;

      if (!histogram) {
        histogram = new Histogram({
          name: metricName,
          help: `Histogram metric for ${name}`,
          labelNames: Object.keys(labels || {}),
          buckets: [0.1, 0.5, 1, 2, 5, 10, 30, 60, 120, 300, 600],
          registers: [this.registry],
        });
        this.metrics.set(metricName, histogram);
      }

      histogram.observe(labels || {}, value);
    } catch (error) {
      this.logger.error(`Failed to record histogram: ${name}`, error);
    }
  }

  /**
   * 记录摘要指标
   */
  summary(name: string, value: number, labels?: Record<string, string>): void {
    try {
      const metricName = this.getMetricName(name);
      let summary = this.metrics.get(metricName) as Summary<string>;

      if (!summary) {
        summary = new Summary({
          name: metricName,
          help: `Summary metric for ${name}`,
          labelNames: Object.keys(labels || {}),
          percentiles: [0.5, 0.9, 0.95, 0.99],
          registers: [this.registry],
        });
        this.metrics.set(metricName, summary);
      }

      summary.observe(labels || {}, value);
    } catch (error) {
      this.logger.error(`Failed to record summary: ${name}`, error);
    }
  }

  /**
   * 记录仪表盘指标
   */
  gauge(name: string, value: number, labels?: Record<string, string>): void {
    try {
      const metricName = this.getMetricName(name);
      let gauge = this.metrics.get(metricName) as Gauge<string>;

      if (!gauge) {
        gauge = new Gauge({
          name: metricName,
          help: `Gauge metric for ${name}`,
          labelNames: Object.keys(labels || {}),
          registers: [this.registry],
        });
        this.metrics.set(metricName, gauge);
      }

      gauge.set(labels || {}, value);
    } catch (error) {
      this.logger.error(`Failed to record gauge: ${name}`, error);
    }
  }

  /**
   * 开始计时
   */
  startTimer(name: string, labels?: Record<string, string>): Timer {
    const timerId = `${name}_${JSON.stringify(labels || {})}`;
    this.timers.set(timerId, Date.now());

    return {
      end: (endLabels?: Record<string, string>) => {
        const startTime = this.timers.get(timerId);
        if (startTime) {
          const duration = Date.now() - startTime;
          this.timers.delete(timerId);
          this.histogram(name, duration / 1000, endLabels || labels);
          return duration;
        }
        return 0;
      },
    };
  }

  /**
   * 记录执行时间
   */
  async time<T>(
    name: string,
    fn: () => T | Promise<T>,
    labels?: Record<string, string>,
  ): Promise<T> {
    const timer = this.startTimer(name, labels);
    try {
      const result = await fn();
      return result;
    } finally {
      timer.end(labels);
    }
  }

  /**
   * 获取指标值
   */
  async getMetric(
    name: string,
    labels?: Record<string, string>,
  ): Promise<number | null> {
    try {
      const metricName = this.getMetricName(name);
      const metric = this.metrics.get(metricName);

      if (metric) {
        const metricData = metric.get();
        const data = await metricData;
        if (data && data.values) {
          const value = data.values.find(
            (v) =>
              !labels ||
              Object.keys(labels).every((key) => v.labels[key] === labels[key]),
          );
          return value ? value.value : null;
        }
      }

      return null;
    } catch (error) {
      this.logger.error(`Failed to get metric: ${name}`, error);
      return null;
    }
  }

  /**
   * 获取所有指标
   */
  async getMetrics(): Promise<string> {
    try {
      return await this.registry.metrics();
    } catch (error) {
      this.logger.error('Failed to get metrics', error);
      return '';
    }
  }

  /**
   * 重置指标
   */
  reset(name?: string): void {
    try {
      if (name) {
        const metricName = this.getMetricName(name);
        const metric = this.metrics.get(metricName);
        if (metric) {
          metric.reset();
        }
      } else {
        this.registry.clear();
        this.metrics.clear();
        this.timers.clear();
        this.initializeRegistry();
      }
    } catch (error) {
      this.logger.error(`Failed to reset metrics: ${name || 'all'}`, error);
    }
  }

  /**
   * 获取注册表
   */
  getRegistry(): Registry {
    return this.registry;
  }

  /**
   * 获取指标列表
   */
  getMetricList(): string[] {
    return Array.from(this.metrics.keys());
  }

  /**
   * 检查指标是否存在
   */
  hasMetric(name: string): boolean {
    const metricName = this.getMetricName(name);
    return this.metrics.has(metricName);
  }

  /**
   * 删除指标
   */
  removeMetric(name: string): boolean {
    const metricName = this.getMetricName(name);
    const metric = this.metrics.get(metricName);
    if (metric) {
      this.registry.removeSingleMetric(metricName);
      this.metrics.delete(metricName);
      return true;
    }
    return false;
  }

  /**
   * 获取指标数据
   */
  getMetricData(name: string): any {
    try {
      const metricName = this.getMetricName(name);
      const metric = this.metrics.get(metricName);
      return metric ? metric.get() : null;
    } catch (error) {
      this.logger.error(`Failed to get metric data: ${name}`, error);
      return null;
    }
  }

  /**
   * 设置指标帮助文本
   */
  setMetricHelp(name: string, help: string): void {
    try {
      const metricName = this.getMetricName(name);
      const metric = this.metrics.get(metricName);
      if (metric && 'help' in metric) {
        (metric as any).help = help;
      }
    } catch (error) {
      this.logger.error(`Failed to set metric help: ${name}`, error);
    }
  }

  /**
   * 获取指标名称
   */
  private getMetricName(name: string): string {
    const prefix = this.config.prefix || 'app';
    return `${prefix}_${name}`;
  }

  /**
   * 创建自定义指标
   */
  createCounter(
    name: string,
    help: string,
    labelNames: string[] = [],
  ): Counter<string> {
    const metricName = this.getMetricName(name);
    const counter = new Counter({
      name: metricName,
      help,
      labelNames,
      registers: [this.registry],
    });
    this.metrics.set(metricName, counter);
    return counter;
  }

  /**
   * 创建自定义直方图
   */
  createHistogram(
    name: string,
    help: string,
    labelNames: string[] = [],
    buckets: number[] = [0.1, 0.5, 1, 2, 5, 10, 30, 60, 120, 300, 600],
  ): Histogram<string> {
    const metricName = this.getMetricName(name);
    const histogram = new Histogram({
      name: metricName,
      help,
      labelNames,
      buckets,
      registers: [this.registry],
    });
    this.metrics.set(metricName, histogram);
    return histogram;
  }

  /**
   * 创建自定义摘要
   */
  createSummary(
    name: string,
    help: string,
    labelNames: string[] = [],
    percentiles: number[] = [0.5, 0.9, 0.95, 0.99],
  ): Summary<string> {
    const metricName = this.getMetricName(name);
    const summary = new Summary({
      name: metricName,
      help,
      labelNames,
      percentiles,
      registers: [this.registry],
    });
    this.metrics.set(metricName, summary);
    return summary;
  }

  /**
   * 创建自定义仪表盘
   */
  createGauge(
    name: string,
    help: string,
    labelNames: string[] = [],
  ): Gauge<string> {
    const metricName = this.getMetricName(name);
    const gauge = new Gauge({
      name: metricName,
      help,
      labelNames,
      registers: [this.registry],
    });
    this.metrics.set(metricName, gauge);
    return gauge;
  }
}
